package com.tiancheng.trade.payment.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tiancheng.trade.commom.core.data.PageInfo;
import com.tiancheng.trade.commom.core.enums.SettleRuleTypeEnums;
import com.tiancheng.trade.commom.core.exception.error.BusinessErrorInfoEnum;
import com.tiancheng.trade.commom.web.model.request.OrderPayDTO;
import com.tiancheng.trade.commom.web.model.response.SettlementRuleResVO;
import com.tiancheng.trade.payment.bo.SettleFlowDetailMerchantBO;
import com.tiancheng.trade.payment.bo.notify.PayNotifySubBO;
import com.tiancheng.trade.payment.channel.bo.PaymentFlowDetailBO;
import com.tiancheng.trade.commom.core.enums.SettleStatusEnum;
import com.tiancheng.trade.payment.mapper.PaymentFlowDetailMapper;
import com.tiancheng.trade.payment.model.PaymentFlowDetail;
import com.tiancheng.trade.payment.service.PaymentFlowDetailService;
import com.tiancheng.trade.payment.service.SettleMatchService;
import com.tiancheng.trade.payment.util.ObjectConvertUtil;
import com.tiancheng.trade.payment.util.helper.PaymentIdHelper;
import com.tiancheng.trade.payment.vo.admin.res.PaymentFlowDetailResVo;
import com.tiancheng.trade.payment.vo.api.QueryFlowDetailVO;
import com.tiancheng.trade.payment.vo.api.UnifiedOrderVO;
import com.tiancheng.trade.payment.vo.api.UnifiedSubOrderVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.sql.Timestamp;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 支付流水明细业务接口实现
 *
 * @author kellen
 * @date 2024/4/29
 */
@Slf4j
@Service
public class PaymentFlowDetailServiceImpl extends ServiceImpl< PaymentFlowDetailMapper, PaymentFlowDetail> implements PaymentFlowDetailService {

    @Resource
    private PaymentFlowDetailMapper paymentFlowDetailMapper;
    @Resource
    private SettleMatchService settleMatchService;

    @Override
    public List<PaymentFlowDetail> save(String flowId, String paymentId, OrderPayDTO unifiedOrderVO, Map<String, SettlementRuleResVO> merchantRuleMap) {
        List<PaymentFlowDetail> flowDetails = new ArrayList<>();
        int subFlowInc = 1; // 子订单流水起始值
        for (OrderPayDTO.SubOrderPayDTO subOrder : unifiedOrderVO.getSubOrderPayDTOS()) {
            PaymentFlowDetail flowDetail = new PaymentFlowDetail();
            BeanUtils.copyProperties(subOrder, flowDetail);
            flowDetail.setFlowId(flowId);
            flowDetail.setPaymentId(paymentId);
//            flowDetail.setSubOrderId(subOrder.getSubOrderId());
//            flowDetail.setMerchantCode(subOrder.getMerchantCode());
//            flowDetail.setMerchantName(subOrder.getMerchantName());
//            flowDetail.setProductCode(subOrder.getProductCode());
//            flowDetail.setProductId(subOrder.getProductId());
//            flowDetail.setProductName(subOrder.getProductName());
//            flowDetail.setProductCount(subOrder.getProductCount());
//            flowDetail.setProductDetail(subOrder.getProductDetail());
//            flowDetail.setOrderAmount(subOrder.getOrderAmount());
//            flowDetail.setAmount(subOrder.getAmount());
            SettlementRuleResVO merchantRules = merchantRuleMap.get(subOrder.getMerchantCode());
            BusinessErrorInfoEnum.CHANNEL_PAY_FAIL.assertIsTrue(null!=merchantRules);
            String settleRuleType = merchantRules.getSettleRuleType().equals(SettleRuleTypeEnums.merchant.getCode())
                    ? SettleRuleTypeEnums.merchant.getCode() : SettleRuleTypeEnums.product.getCode();
            flowDetail.setSettleRuleCode(settleRuleType + merchantRules.getSettleRuleCode());
            // 子订单优惠金额
            flowDetail.setPlatformDiscount(null == subOrder.getPlatformDiscount() ? 0 : subOrder.getPlatformDiscount());
            flowDetail.setDiscount(flowDetail.getPlatformDiscount());
            // 子订单流水
            flowDetail.setSubFlowId(PaymentIdHelper.getSubOrderFlowId(flowId, subFlowInc));
            flowDetail.setCouponSettleStatus("");
            flowDetails.add(flowDetail);
            subFlowInc++;
        }
        paymentFlowDetailMapper.save(flowDetails);
        return flowDetails;
    }

    @Override
    public PageInfo find(QueryFlowDetailVO queryFlowDetailVO) {
        if (queryFlowDetailVO.getEndTime() == null) {
            queryFlowDetailVO.setEndTime(LocalDateTime.now());
        }
        if (queryFlowDetailVO.getStartTime() == null) {
            queryFlowDetailVO.setStartTime(queryFlowDetailVO.getEndTime().minusMonths(1).withHour(0).withMinute(0).withSecond(0));
        }

        IPage<Map> page = paymentFlowDetailMapper.find(queryFlowDetailVO,
                new Page<>(queryFlowDetailVO.getCurrent(), queryFlowDetailVO.getSize()));
        return paymentFlowDetailMapper.convertPage(page);
    }

    /**
     * 聚合应清分商户、商品编码
     *
     * @param expectSettleDate 渠道应核销日期
     * @return
     */
    public List<PaymentFlowDetail> distinctSettle(LocalDate expectSettleDate, String channelCode) {
        return paymentFlowDetailMapper.distinctSettle(expectSettleDate, channelCode);
    }

    @Override
    public List<SettleFlowDetailMerchantBO> distinctSettleMerchant(LocalDate expectSettleDate, String payChannel) {
        return paymentFlowDetailMapper.distinctSettleMerchant(expectSettleDate, payChannel);
    }

    /**
     * 查询应清分子订单，每次最多查询1000条
     *
     * @param expectSettleDate 渠道应核销日期
     * @param channel          支付渠道
     * @param startId          起始Id
     * @return
     */
    public List<PaymentFlowDetailBO> listSettle(LocalDate expectSettleDate, String channel, Long startId) {
        return paymentFlowDetailMapper.listSettle(expectSettleDate, channel, startId);
    }

    /**
     * 流水明细批量更新-所更新字段值相同
     *
     * @param ids       id
     * @param colValMap 字段-值：(column, value)
     * @return
     */
    public int updateSettleBatch(List<Long> ids, Map<String, Object> colValMap) {
        return paymentFlowDetailMapper.updateSettleBatch(ids, colValMap);
    }



    /**
     * 流水明细批量更新-所更新字段值相同
     *
     * @param subFlowIds       子流水号集合
     * @param colValMap 字段-值：(column, value)
     * @return
     */
    public int updateSettleBatchBySubFlowId(List<String> subFlowIds, Map<String, Object> colValMap){
        return paymentFlowDetailMapper.updateSettleBatchBySubFlowId(subFlowIds,colValMap);
    }

    @Override
    public List<PaymentFlowDetail> findByFlowId(String flowId) {
        if (StringUtils.isEmpty(flowId)) {
            return null;
        }
        QueryWrapper<PaymentFlowDetail> conditions = new QueryWrapper<>();
        conditions.eq("flow_id", flowId);
        return paymentFlowDetailMapper.selectList(conditions);
    }

    @Override
    public List<PaymentFlowDetailResVo> findSettleByFlowId(String flowId) {
        if (StringUtils.isEmpty(flowId)) {
            return Collections.emptyList();
        }
        List<PaymentFlowDetail> paymentFlowDetails = this.findByFlowId(flowId);
        List<String> subOrderIds = paymentFlowDetails.stream().map(PaymentFlowDetail::getSubOrderId).collect(Collectors.toList());
        List<Map<String, Object>> settleDetails = settleMatchService.findBySubOrderIds(subOrderIds, SettleStatusEnum.SUCCESS.getCode());
        List<PaymentFlowDetailResVo> result = new ArrayList<>();
        for (PaymentFlowDetail paymentFlowDetail : paymentFlowDetails) {
            PaymentFlowDetailResVo p = ObjectConvertUtil.convert(paymentFlowDetail, PaymentFlowDetailResVo.class).get();
            if (CollectionUtils.isEmpty(settleDetails)) {
                result.add(p);
                continue;
            }
            List<Map<String, Object>> subOrderSettleMatch = settleDetails.stream().filter(s -> s.get("sub_order_id").toString().equals(paymentFlowDetail.getSubOrderId())).collect(Collectors.toList());
            if (CollectionUtils.isEmpty(subOrderSettleMatch) || subOrderSettleMatch.get(0).get("update_time") == null) {
                result.add(p);
                continue;
            }
            LocalDateTime updateTime = Instant.ofEpochMilli(((Timestamp) subOrderSettleMatch.get(0).get("update_time")).getTime()).atZone(ZoneOffset.ofHours(8)).toLocalDateTime();
            p.setExpectSettleSuccessDate(updateTime);
            result.add(p);
        }
        return result;
    }

    @Override
    public List<PayNotifySubBO> updateAmount(String flowId, Integer couponAmount) {
        List<PaymentFlowDetail> flowDetails = this.findByFlowId(flowId);
        final List<PayNotifySubBO> payNotifySubBOS = new ArrayList<PayNotifySubBO>(flowDetails.size());
        if (!CollectionUtils.isEmpty(flowDetails) && null != couponAmount && couponAmount > 0) {
            log.info("更新流水优惠信息：flowId：{}， couponAmount：{}", flowId, couponAmount);
            //总订单金额
            try {
                int sumOrderAmount = flowDetails.stream().mapToInt(PaymentFlowDetail::getOrderAmount).sum();
                int totalPlatformDiscount = flowDetails.stream().peek(item -> {
                    if (item.getPlatformDiscount() == null) {
                        item.setPlatformDiscount(0);
                    }
                }).filter(item -> item.getPlatformDiscount() > 0).mapToInt(PaymentFlowDetail::getPlatformDiscount).sum();
                int discountTotal = 0;
                for (int i = 0; i < flowDetails.size(); i++) {
                    PaymentFlowDetail flowDetail = flowDetails.get(i);
                    PaymentFlowDetail updateDetail = new PaymentFlowDetail();
                    QueryWrapper<PaymentFlowDetail> conditions = new QueryWrapper<>();
                    conditions.eq("id", flowDetails.get(i).getId());
                    conditions.eq("flow_id", flowId);
                    if (totalPlatformDiscount == 0) {
                        // 无平台优惠
                        if (i == flowDetails.size() - 1) {
                            updateDetail.setAmount(flowDetail.getOrderAmount() - (couponAmount - discountTotal));
                            updateDetail.setChannelDiscount(couponAmount - discountTotal);
                            updateDetail.setDiscount(updateDetail.getChannelDiscount());
                        } else {
                            // 1、计算子订单所占总金额的比例，小数点后取10位，然后截取
                            // 2、总优惠金额*子订单比例，然后四舍五入取整
                            int divide = BigDecimal.valueOf(flowDetail.getOrderAmount()).
                                    divide(BigDecimal.valueOf(sumOrderAmount), 10, BigDecimal.ROUND_DOWN)
                                    .multiply(BigDecimal.valueOf(couponAmount)).setScale(0, BigDecimal.ROUND_HALF_UP).intValue();
                            updateDetail.setAmount(flowDetail.getOrderAmount() - divide);
                            updateDetail.setDiscount(divide);
                            updateDetail.setChannelDiscount(divide);
                            discountTotal += divide;
                        }
                    } else {
                        // 有平台优惠，按照扣除掉平台优惠金额的订单金额来做分摊
                        if (i == flowDetails.size() - 1) {
                            updateDetail.setAmount(flowDetail.getOrderAmount() - flowDetail.getPlatformDiscount() - (couponAmount - discountTotal));
                            updateDetail.setChannelDiscount(couponAmount - discountTotal);
                            updateDetail.setDiscount(flowDetail.getPlatformDiscount() + updateDetail.getChannelDiscount());
                        } else {
                            // 1、计算子订单所占总金额的比例，小数点后取10位，然后截取
                            // 2、总优惠金额*子订单比例，然后四舍五入取整
                            int divide = BigDecimal.valueOf(flowDetail.getOrderAmount() - flowDetail.getPlatformDiscount()).
                                    divide(BigDecimal.valueOf(sumOrderAmount - totalPlatformDiscount), 10, BigDecimal.ROUND_DOWN)
                                    .multiply(BigDecimal.valueOf(couponAmount)).setScale(0, BigDecimal.ROUND_HALF_UP).intValue();
                            updateDetail.setAmount(flowDetail.getOrderAmount() - flowDetail.getPlatformDiscount() - divide);
                            updateDetail.setDiscount(flowDetail.getPlatformDiscount() + divide);
                            updateDetail.setChannelDiscount(divide);
                            discountTotal += divide;
                        }
                    }

                    paymentFlowDetailMapper.update(updateDetail, conditions);
                    final PayNotifySubBO payNotifySubBO = new PayNotifySubBO();
                    payNotifySubBO.setSubOrderId(flowDetail.getSubOrderId());
                    payNotifySubBO.setFlowId(flowDetail.getFlowId());
                    payNotifySubBO.setSubFlowId(flowDetail.getSubFlowId());
                    payNotifySubBO.setAmount(updateDetail.getAmount());
                    payNotifySubBO.setDiscount(updateDetail.getDiscount());
                    payNotifySubBO.setChannelDiscount(updateDetail.getChannelDiscount());
                    payNotifySubBOS.add(payNotifySubBO);
                }
            } catch (Exception e) {
                e.printStackTrace();
                log.error("更新流水优惠异常：{}", e.getMessage());
                throw e;
            }
        }else{
            for (PaymentFlowDetail flowDetail : flowDetails) {
                final PayNotifySubBO payNotifySubBO = new PayNotifySubBO();
                payNotifySubBO.setSubOrderId(flowDetail.getSubOrderId());
                payNotifySubBO.setFlowId(flowDetail.getFlowId());
                payNotifySubBO.setSubFlowId(flowDetail.getSubFlowId());
                payNotifySubBO.setAmount(flowDetail.getAmount());
                payNotifySubBO.setDiscount(flowDetail.getDiscount());
                payNotifySubBO.setChannelDiscount(flowDetail.getChannelDiscount());
                payNotifySubBOS.add(payNotifySubBO);
            }
        }
        return payNotifySubBOS;
    }

    @Override
    public List<PaymentFlowDetail> selectList(QueryWrapper<PaymentFlowDetail> condition) {
        return paymentFlowDetailMapper.selectList(condition);
    }

    @Override
    public int updateEnterpriseCodeByFlowIdAndSubOrderId(List<PaymentFlowDetail> flowDetails) {
        if (CollectionUtils.isEmpty(flowDetails)) {
            return 0;
        }
        paymentFlowDetailMapper.updateMerchantBatch(flowDetails);
        return 1;
    }

    @Override
    public List<PaymentFlowDetail> queryCouponSettleMerchant(LocalDateTime expectSettleTimeBegin, LocalDateTime expectSettleTimeEnd) {
        return paymentFlowDetailMapper.queryCouponSettleMerchant(expectSettleTimeBegin, expectSettleTimeEnd);
    }

    @Override
    public List<PaymentFlowDetailBO> queryCouponSettlePayFlowDetail(LocalDateTime expectSettleTimeBegin, LocalDateTime expectSettleTimeEnd, long startId) {
        return paymentFlowDetailMapper.queryCouponSettlePayFlowDetail(expectSettleTimeBegin, expectSettleTimeEnd, startId);
    }

    @Override
    public List<PaymentFlowDetail> queryBySubFlowIds(List<String> subFLowIds) {
        return this.list(new LambdaQueryWrapper<PaymentFlowDetail>().in(PaymentFlowDetail::getSubFlowId,subFLowIds));
    }

    @Override
    public List<PaymentFlowDetailBO> queryDetailBOBySubFlowIds(Set<String> subFlowIds) {
        return this.getBaseMapper().queryBySubFlowIds(subFlowIds);
    }

}
